package com.zlc.workflow.flow.node;


import com.zlc.workflow.constant.NodeType;
import com.zlc.workflow.flow.Model;
import com.zlc.workflow.flow.NodeModel;
import com.zlc.workflow.flow.TaskModel;
import com.zlc.workflow.flow.check.NodeCache;
import org.springframework.util.ObjectUtils;

import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.zlc.workflow.constant.NodeType.TASK;

/**
 * 流程定义模型
 *
 * @author zhanglei
 */
public class ProcessModel extends BaseNode {

    /**
     * 流程定义ID号
     */
    private String processId;

    /**
     * 子节点
     */
    private NodeModel childNode;

    /**
     * 流程编号
     */
    private String processCode;

    /**
     * 流程名称
     */
    private String processName;

    /**
     * 所有任务节点
     */
    private List<NodeModel>  allTaskNodes = new LinkedList<>();

    /**
     * 所有节点
     */
    protected List<NodeModel> allNodes = new LinkedList<>();


    /**
     * 第一个任务节点
     */
    private TaskNode firstTaskNode;


    /**
     * 最后一个任务节点
     */
    private List<NodeModel>  lastTaskNode = new LinkedList<>();

    /**
     * 最起始节点
     */
    private NodeModel startNode;



    public ProcessModel(String processId,String processCode, String processName) {
        super(processId,processCode, processName);
        this.processCode = processCode;
        this.processId = processId;
        this.processName = processName;
        this.nodeType = NodeType.PROCESS;
    }

    public List<NodeModel> getAllTaskNodes() {
        return allTaskNodes;
    }

    public void setAllTaskNodes(List<NodeModel> allTaskNodes) {
        this.allTaskNodes = allTaskNodes;
    }

    public TaskNode getFirstTaskNode() {
        return firstTaskNode;
    }

    public void setFirstTaskNode(TaskNode firstTaskNode) {
        this.firstTaskNode = firstTaskNode;
    }

    public List<NodeModel> getLastTaskNode() {
        return lastTaskNode;
    }

    public void setLastTaskNode(List<NodeModel> lastTaskNode) {
        this.lastTaskNode = lastTaskNode;
    }

    public String getProcessId() {
        return processId;
    }

    public void setProcessId(String processId) {
        this.processId = processId;
    }

    public String getProcessCode() {
        return processCode;
    }

    public void setProcessCode(String processCode) {
        this.processCode = processCode;
    }

    public void setProcessName(String processName) {
        this.processName = processName;
    }

    public NodeModel getChildNode() {
        return childNode;
    }

    public void setChildNode(NodeModel nodeModel) {
        this.childNode = nodeModel;
        this.startNode = nodeModel;
        enhanceProcessModel();
    }

    public NodeModel getNodeModel(String nodeName){
        for(NodeModel nodeModel : allNodes){
            if(nodeName.equals(nodeModel.nodeId())){
                return nodeModel;
            }
        }
        return null;
    }

    public TaskModel getTaskModel(String nodeName){
        for(NodeModel nodeModel : allTaskNodes){
            if(nodeName.equals(nodeModel.nodeId())){
                return (TaskModel) nodeModel;
            }
        }
        return null;
    }

    @SuppressWarnings("unchecked")
    public <T>T getNodeModel(String nodeName,Class<T> clazz){
        NodeModel nodeModel = getNodeModel(nodeName);
        if(!ObjectUtils.isEmpty(nodeModel)){
            if(clazz.isInstance(nodeModel)) {
                return (T) nodeModel;
            }
        }
        return null;
    }


    /**
     * 增强模型
     */
    public void enhanceProcessModel(){
        buildAllNodes(this.childNode);
        buildAllTaskNodes(this.childNode);
        buildFirstTaskNode();
        //设置前置任务节点
        for(NodeModel node : this.allNodes){
            createPreTaskNodes(node,node);
        }
        buildLastTaskNodes();
    }


    private void buildAllNodes(NodeModel nodeModel){
        Map<String, Model> models = NodeCache.getInstance().get();
        for(Map.Entry<String,Model> model : models.entrySet()){
            Model m = model.getValue();
            if(m instanceof NodeModel){
                this.allNodes.add((NodeModel) m);
            }
        }
    }

    private void buildAllTaskNodes(NodeModel nodeModel){
        Map<String,Model> models = NodeCache.getInstance().get();
        for(Map.Entry<String,Model> model : models.entrySet()){
            Model m = model.getValue();
            if(m instanceof TaskNode){
                this.allTaskNodes.add((TaskNode) m);
            }
        }
    }


    private boolean isTaskModel(NodeModel nodeModel){
        String type = nodeModel.nodeType().value();
        return TASK.value().equals(type);
    }


    private void buildFirstTaskNode(){
        NodeModel firstNode = childNode.nextTask().get(0);
        if(firstNode instanceof TaskNode){
            TaskNode taskNode  = (TaskNode)firstNode;
            taskNode.setFirstTask(true);
            this.setFirstTaskNode(taskNode);
        }
    }


    private void buildLastTaskNodes(){
        if(!ObjectUtils.isEmpty(this.allNodes)){
            List<NodeModel> nodeModels = this.allNodes.stream().filter(
                    node-> NodeType.END.value().equals(node.nodeType().value())).collect(Collectors.toList());
            if(!ObjectUtils.isEmpty(nodeModels)) {
                this.lastTaskNode = nodeModels.get(0).preTask();
                lastTaskBool(this.lastTaskNode);
                return;
            }
        }
        buildLastTaskNodes(this.childNode);
    }

    private void  lastTaskBool(List<NodeModel> lastTaskNode){
        for(NodeModel nodeModel : lastTaskNode){
            if(nodeModel instanceof TaskNode){
                ((TaskNode) nodeModel).setLastTask(true);
            }
        }
    }


    private void  buildLastTaskNodes(NodeModel nodeModel){
        List<NodeModel> nextNodes = nodeModel.next();

        if(!ObjectUtils.isEmpty(nextNodes)){
            for(NodeModel node : nextNodes){
                if(NodeType.END.value().equals(node.nodeType().value())){
                    this.lastTaskNode = node.preTask();
                    lastTaskBool(this.lastTaskNode);
                    return;
                }else{
                    buildLastTaskNodes(node);
                }
            }
        }
    }




    public List<TaskModel> getNextTasks(String curNodeId){
        NodeModel curNode = getNodeModel(curNodeId);
        if(ObjectUtils.isEmpty(curNode)){
            return Collections.emptyList();
        }

        List<TaskModel> taskModels = new LinkedList<>();
        addNextTasks(taskModels,curNode);
        return taskModels;
    }


    private void  addNextTasks(List<TaskModel> taskModels,NodeModel curNode){
        List<NodeModel> nextModels = curNode.next();

        for(NodeModel task : nextModels){
            if(task instanceof TaskModel) {
                if (!taskModels.contains(task)) {
                    taskModels.add((TaskModel) task);
                }
                addNextTasks(taskModels,task);
            }
        }
    }


    public List<TaskModel> getLastTasks(String curNodeId){
        NodeModel curNode = getNodeModel(curNodeId);
        if(ObjectUtils.isEmpty(curNode)){
            curNode = getTaskModel(curNodeId);
            if(ObjectUtils.isEmpty(curNode)) {
                return Collections.emptyList();
            }
        }

        List<TaskModel> taskModels = new LinkedList<>();
        addLastTasks(taskModels,curNode);
        return taskModels;
    }




    public void addLastTasks(List<TaskModel> taskModels,NodeModel curNode){
        List<NodeModel> lastModels = curNode.pre();

        for(NodeModel task : lastModels){
            if(task instanceof TaskModel) {
                if (!taskModels.contains(task)) {
                    taskModels.add((TaskModel) task);
                }
            }
            addLastTasks(taskModels,task);
        }
    }


    /**
     * 判断任务节点是否在指定节点后面
     * @param curTask    指定任务节点
     * @param nextTask   判断的任务节点
     * @return           boolean
     */
    public boolean isNextTask(String curTask,String nextTask){
        List<TaskModel> nextTasks = getNextTasks(curTask);
        for(TaskModel taskModel : nextTasks){
            if(taskModel.nodeId().equals(nextTask)){
                return true;
            }
        }
        return false;
    }

    /**
     * 判断任务节点是否在指定节点前面
     * @param curTask    指定任务节点
     * @param lastTask   判断的任务节点
     * @return           boolean
     */
    public boolean isPreTask(String curTask,String lastTask){
        List<TaskModel> lastTasks = getLastTasks(curTask);
        for(TaskModel taskModel : lastTasks){
            if(taskModel.nodeId().equals(lastTask)){
                return true;
            }
        }
        return false;
    }



    public List<NodeModel> getAllNodes() {
        return allNodes;
    }

    public void setAllNodes(List<NodeModel> allNodes) {
        this.allNodes = allNodes;
    }

    public NodeModel getStartNode() {
        return startNode;
    }

    public void setStartNode(NodeModel startNode) {
        this.startNode = startNode;
    }

    public String getProcessName() {
        return processName;
    }
}